#ifndef __LS_LOGGER_H__
#define __LS_LOGGER_H__


#include "level.hpp"
#include "formatter.hpp"
#include "sink.hpp"
#include "asyncworker.hpp"
#include <atomic>
#include <mutex>
#include <unordered_map>
#include <cstdarg>

namespace log_system
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        
        Logger(const std::string& loggerName,
            LogLevel::value level,
            Formatter::ptr formatter,
            std::vector<LogSink::ptr>& sinks):
            _loggerName(loggerName),
            _limitLevel(level),
            _formatter(formatter),
            _sinks(sinks.begin(), sinks.end())
        {}

        const std::string& name()
        {
            return _loggerName;
        }

        //构造日志消息对象并格式化得到日志消息字符串，再进行落地
        void debugLog(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            //判断是否达到输出等级
            if(LogLevel::value::DEBUG < _limitLevel) return;
                        
            va_list ap;
            va_start(ap, fmt);
            writeLog(LogLevel::value::DEBUG, file, line, fmt, ap);
            va_end(ap);
        }
        void infoLog(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            //判断是否达到输出等级
            if(LogLevel::value::INFO < _limitLevel) return;

            va_list ap;
            va_start(ap, fmt);
            writeLog(LogLevel::value::INFO, file, line, fmt, ap);
            va_end(ap);
        }
        void warnLog(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            //判断是否达到输出等级
            if(LogLevel::value::WARN < _limitLevel) return;

            va_list ap;
            va_start(ap, fmt);
            writeLog(LogLevel::value::WARN, file, line, fmt, ap);
            va_end(ap);
        }
        void errorLog(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            //判断是否达到输出等级
            if(LogLevel::value::ERROR < _limitLevel) return;

            va_list ap;
            va_start(ap, fmt);
            writeLog(LogLevel::value::ERROR, file, line, fmt, ap);
            va_end(ap);
        }
        void fatalLog(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            //判断是否达到输出等级
            if(LogLevel::value::FATAL < _limitLevel) return;

            va_list ap;
            va_start(ap, fmt);
            writeLog(LogLevel::value::FATAL, file, line, fmt, ap);
            va_end(ap);            
        }
    protected:
        void writeLog(LogLevel::value level, const std::string& file, size_t line, const std::string& fmt, va_list& ap)
        {
            //对fmt格式化字符串和不定参进行组织，得到日志消息主体部分字符串
            va_list ap_copy;
            va_copy(ap_copy, ap);

            int len = vsnprintf(nullptr, 0, fmt.c_str(), ap_copy);
            std::unique_ptr<char[]> buffer(new char[len+1]);
            int ret = vsnprintf(buffer.get(), len + 1, fmt.c_str(), ap);
            va_end(ap_copy);

            if(ret != len)
            {
                std::cerr << "vsnprintf failed!\n";
                return; 
            }

            //3.构造LogMsg对象
            LogMsg msg(level, file, line, _loggerName, buffer.get());

            //4.使用Formatter根据LogMsg对象得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            std::string str = ss.str();

            //5.进行日志落地
            log(str.c_str(), str.size());
        }
        //负责实际落地，不同的日志器有不同的落地方式
        virtual void log(const char* data, size_t len) = 0;
    protected:
        std::mutex _mutex;
        std::string _loggerName;
        std::atomic<LogLevel::value> _limitLevel;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string& loggerName,
            LogLevel::value level,
            Formatter::ptr formatter,
            std::vector<LogSink::ptr>& sinks):
            Logger(loggerName, level, formatter, sinks)
            {}
    protected:
        void log(const char* data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            for(auto& sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };


    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string& loggerName,
            LogLevel::value level,
            Formatter::ptr formatter,
            std::vector<LogSink::ptr>& sinks,
            AsyncWorkerType type):
            Logger(loggerName, level, formatter, sinks),
            _worker(std::make_shared<AsyncWorker>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), type))
        {}


        void log(const char* data, size_t len) override
        {
            _worker->push(data, len);
        }

    private:
        void realLog(Buffer& buf)
        {
            for(auto& sink : _sinks)
            {
                sink->log(buf.data(), buf.readableSize());
            }
        }
    private:
        AsyncWorker::ptr _worker;
    };






    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    // 使用建造者模式来建造日志器，简化用户的使用复杂度
    // 1.抽象一个日志器建造者类（完成日志器对象所需零部件的构建 & 日志器的构建）
    //  1.设置日志器类型
    //  2.根据日志器类型来建造同步和异步两种不同类型的日志器
    // 2.派生出具体的建造者类--局部日志器建造者和全局日志器建造者

    class LoggerBuilder
    {
    public:
        LoggerBuilder():
            _asyncType(AsyncWorkerType::ASYNC_SAFE),
            _loggerType(LoggerType::LOGGER_SYNC),
            _limitLevel(LogLevel::value::DEBUG)
        {}

        void enableUnsafeAsync()    { _asyncType = AsyncWorkerType::ASYNC_UNSAFE; }
        void buildLoggerType(LoggerType type)   { _loggerType = type; }
        void buildLoggerName(const std::string& loggerName) { _loggerName = loggerName; }
        void buildLoggerLevel(LogLevel::value level)    { _limitLevel = level; }
        void buildFormatter(const std::string& pattern) { _formatter = std::make_shared<Formatter>(pattern); }

        template<typename SinkType, typename ...Args>
        void buildSink(Args&& ...args)
        {
            LogSink::ptr pSink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(pSink);
        }

        virtual Logger::ptr build() = 0;
    protected:
        AsyncWorkerType _asyncType;
        LoggerType _loggerType;
        std::string _loggerName;
        LogLevel::value _limitLevel;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:

        Logger::ptr build() override
        {
            assert(!_loggerName.empty());   //必须有日志器名称
            if(_formatter.get() == nullptr) _formatter = std::make_shared<Formatter>();

            if(_sinks.empty())  buildSink<StdoutSink>();

            if(_loggerType == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_loggerName, _limitLevel, _formatter, _sinks, _asyncType);
            }
            return std::make_shared<SyncLogger>(_loggerName, _limitLevel, _formatter, _sinks);

        }
    };

    class LoggerManager
    {

    public:
        static LoggerManager* getInstance()
        {
            static LoggerManager ins;
            return &ins;
        }

        void addLogger(Logger::ptr& logger)
        {
            if(hasLogger(logger->name()))   return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }

        bool hasLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            auto it = _loggers.find(name);
            
            return it != _loggers.end();
        }

        Logger::ptr getLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            auto it = _loggers.find(name);
            if(it == _loggers.end())
                return Logger::ptr();
            return it->second;
        }

        Logger::ptr defaultLogger()
        {
            return _defaultLogger;
        }


    private:
        LoggerManager()
        {
            std::unique_ptr<log_system::LoggerBuilder> builder(new log_system::LocalLoggerBuilder());
            builder->buildLoggerName("defautLogger");
            _defaultLogger = builder->build();
            _loggers.insert(std::make_pair(_defaultLogger->name(), _defaultLogger));
        }

    private:
        std::mutex _mutex;
        Logger::ptr _defaultLogger;
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_loggerName.empty());   //必须有日志器名称
            if(_formatter.get() == nullptr) _formatter = std::make_shared<Formatter>();

            if(_sinks.empty())  buildSink<StdoutSink>();

            Logger::ptr logger;
            if(_loggerType == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_loggerName, _limitLevel, _formatter, _sinks, _asyncType);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_loggerName, _limitLevel, _formatter, _sinks);
            }
            LoggerManager::getInstance()->addLogger(logger);

            return logger;
        }
    };


    
}


#endif